using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UI;

/// <summary>
/// UI复用列表坐标变换工具类
/// </summary>
public static class UIReuseListTransformation
{
    /// <summary> 平移矩阵 </summary>
    static readonly Matrix4x4 _OFFSET_MATRIX = new Matrix4x4(Vector3.right, Vector3.up, Vector3.forward, Vector3.zero);

    /// <summary>
    /// 旋转矩阵
    /// cos(θ), -sin(θ)
    /// sin(θ), cos(θ)
    /// </summary>
    static readonly Matrix4x4 _ROTATION_MATRIX = new Matrix4x4(
       new Vector4(1, -1, 0, 0),
       new Vector4(1, 1, 0, 0),
       new Vector4(0, 0, 0, 0),
       new Vector4(0, 0, 0, 0)
    );

    /// <summary>
    /// AchoredPosition 转 WorldPosition
    /// </summary>
    public static Vector3 AnchorePos2WorldPos(Matrix4x4 contentToWorldMatrix, Vector2 contentPivot, Vector2 contentRectSize, Vector2 anchoredPos, Vector2 anchore)
    {
        var localPos = AnchorePos2LocalPos(contentPivot, contentRectSize, anchoredPos, anchore);
        var worldPos = LocalPos2WorldPos(contentToWorldMatrix, localPos);
        return worldPos;
    }


    /// <summary>
    /// AchoredPosition 转 LocalPosition
    /// </summary>
    public static Vector3 AnchorePos2LocalPos(Vector2 contentPivot, Vector2 contentRectSize, Vector2 anchoredPos, Vector2 anchore)
    {
        // 转为Content下的笛卡尔坐标系
        var localPos = contentRectSize * (anchore - contentPivot) + anchoredPos;
        return new Vector3(localPos.x, localPos.y, 0f);
    }

    /// <summary>
    /// LocalPosition 转 WorldPosition
    /// </summary>
    public static Vector3 LocalPos2WorldPos(Matrix4x4 contentToWorldMatrix, Vector3 localPos)
    {
        return contentToWorldMatrix.MultiplyPoint3x4(localPos);
    }

    /// <summary>
    /// WorldPosition 转 LocalPosition
    /// </summary>
    public static Vector3 WorldPos2LocalPos(RectTransform target, Vector3 worldPos)
    {
        return target.worldToLocalMatrix.MultiplyPoint3x4(worldPos);
    }

    public static void GetWorldCornersByAnchore(ref Vector3[] corners, RectTransform content
        , Vector2 anchoredPos, Vector2 size, Vector2 pivot, Vector2 anchor, float angle = 0)
    {
        Assert.IsTrue(corners != null && corners.Length == 4, "ancthoreCorners length has to be 4");
        GetAnchoreCorners(ref corners, anchoredPos, size, pivot);
        RotationCorners(ref corners, angle);

        var contentToWorldMatrix = content.localToWorldMatrix;
        var contentPivot = content.pivot;
        var contentRectSize = content.rect.size;
        for (int i = 0; i < corners.Length; i++)
        {
            corners[i] = AnchorePos2WorldPos(contentToWorldMatrix, contentPivot, contentRectSize, corners[i], anchor);
        }
    }

    /// <summary>
    /// 四个边界角的顶点AnchorePostion
    /// 顺序如下：left_bottom，left_top，right_top， right_bottom
    /// </summary>
    static void GetAnchoreCorners(ref Vector3[] ancthoreCorners, Vector2 anchoredPos, Vector2 size, Vector2 pivot)
    {
        // left_bottom
        ancthoreCorners[0] = new Vector3(anchoredPos.x - (pivot.x) * size.x, anchoredPos.y - (pivot.y) * size.y, 0f);
        // left_top
        ancthoreCorners[1] = new Vector3(anchoredPos.x - (pivot.x) * size.x, anchoredPos.y + (1.0f - pivot.y) * size.y, 0f);
        // right_top
        ancthoreCorners[2] = new Vector3(anchoredPos.x + (1.0f - pivot.x) * size.x, anchoredPos.y + (1.0f - pivot.y) * size.y, 0f);
        // right_bottom
        ancthoreCorners[3] = new Vector3(anchoredPos.x + (1.0f - pivot.x) * size.x, anchoredPos.y - (pivot.y) * size.y, 0f);
    }

    public static Bounds GetBounds(Vector3[] corners, ref Matrix4x4 targetWorldToLocalMatrix, bool is3D = true)
    {
        var vMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
        var vMax = new Vector3(float.MinValue, float.MinValue, float.MinValue);

        for (int j = 0; j < 4; j++)
        {
            Vector3 v = targetWorldToLocalMatrix.MultiplyPoint3x4(corners[j]);
            vMin = Vector3.Min(v, vMin);
            vMax = Vector3.Max(v, vMax);
        }

        if (!is3D) vMin.z = vMax.z = 0;

        var bounds = new Bounds(vMin, Vector3.zero);
        bounds.Encapsulate(vMax);
        return bounds;
    }

    /// <summary>
    /// 旋转
    /// </summary>
    /// <param name="ancthoreCorners"></param>
    /// <param name="angle"> 旋转角度，欧拉角 </param>
    public static void RotationCorners(ref Vector3[] corners, float angle)
    {
        if (angle.Equals(0))
            return;

        Matrix4x4 panMatrix = _ROTATION_MATRIX;
        angle *= Mathf.Deg2Rad;
        panMatrix.m00 = Mathf.Cos(angle);
        panMatrix.m01 = -1 * Mathf.Sin(angle);
        panMatrix.m10 = Mathf.Sin(angle);
        panMatrix.m11 = Mathf.Cos(angle);

        for (int i = 0, length = corners.Length; i < length; i++)
        {
            var corner = corners[i];
            var vect = new Vector4(corner.x, corner.y, 0, 0);
            corners[i] = panMatrix.MultiplyPoint3x4(vect);
        }
    }

    /// <summary>
    /// 平移
    /// </summary>
    /// <param name="ancthoreCorners"></param>
    /// <param name="distance"> 位移位置 </param>
    public static void PanAnchoreCorners(ref Vector3[] corners, Vector3 distance)
    {
        Matrix4x4 panMatrix = _OFFSET_MATRIX;
        panMatrix.m02 = distance.x;
        panMatrix.m12 = distance.y;

        for (int i = 0, length = corners.Length; i < length; i++)
        {
            var corner = corners[i];
            var vect = new Vector4(corner.x, corner.y, 1, 0);
            corners[i] = panMatrix.MultiplyPoint3x4(vect);
        }
    }
    
    /// <summary>
    /// UIGraphic 本地坐标镜像
    /// sign = 1表示镜像x坐标，sign = 2 表示镜像y坐标, sign = 3 表示全部镜像
    /// 默认值为1
    /// </summary>
    public static Vector2 LocalMirror(Graphic ui, Vector2 position, int sign = 1)
    {
        if (sign == 0)
            return position;

        var size = ui.GetPixelAdjustedRect().size;
        var pivot = ui.rectTransform.pivot;
        return LocalMirror(size, pivot, position, sign);
    }
    
    /// <summary>
    /// 本地坐标镜像
    /// sign = 1表示镜像x坐标，sign = 2 表示镜像y坐标, sign = 3 表示全部镜像
    /// 默认值为1
    /// </summary>
    public static Vector2 LocalMirror(Vector2 size, Vector2 pivot, Vector2 position, int sign = 1)
    {
        if (sign == 0)
            return position;

        var offset = -pivot * size;
        var temp = size - position + 2 * offset;
        if ((sign & 0x01) != 0)
        {
            position.x = temp.x;
        }
        if ((sign & 0x02) != 0)
        {
            position.y = temp.y;
        }
        return position;
    }
}

